//
//  HDAPIService.swift
//  Excited
//
//  Created by X140Yu on 7/30/16.
//  Copyright © 2016 X140Yu. All rights reserved.
//

import Foundation
import RxSwift
import Alamofire
import YYKit

public enum HDAPIError: ErrorType {
    case Error
    case ParseError
    case NoData
}

/// use this class to make request
public class HDAPIService {
    static let sharedInstance = HDAPIService()
    private init() { }
    public let provider = HDSchoolAPIProvider
    let ssfwProvider = HDSSFWAPIProvider
    let disposeBag = DisposeBag()

    /**
     获取验证码

     - parameter completion: completion, it's in the main thread
     */
    public func getCaptcha(completion: (UIImage?, HDAPIError?) -> Void) {
        provider
            .request(.GetCookie)
            .subscribe { [unowned self] (event) in
            switch event {
            case .Next(let response):
                guard let res = response.response else { completion(nil, .NoData); return }
                let resHTTP = res as! NSHTTPURLResponse
                guard let cooke = resHTTP.allHeaderFields["Set-Cookie"] as? String else { completion(nil, .NoData); return }
                guard let cookieB = HDCookieManager.sharedInstance.getSecondCookie(cooke) else { completion(nil, .NoData); return }
                HDCookieManager.sharedInstance.saveCookie(cookieB, type: .B)
                debugPrint("-- Save CookieB: " + cookieB + " --")

                self.provider
                    .request(.GetCaptcha)
                    .subscribe { (event) in
                        switch event {
                        case .Next(let response):
                            guard let img = UIImage(data: response.data) else { completion(nil, .NoData); return }
                            dispatch_async(dispatch_get_main_queue(), {
                                completion(img, nil); return
                            })
                            break
                        case .Error(let error):
                            debugPrint(error)
                            completion(nil, .NoData); return
                        case .Completed: break
                        }
                    }.addDisposableTo(self.disposeBag)
                break
            case .Error(let error):
                debugPrint(error)
                completion(nil, .NoData); break
            case .Completed: break
                }
            }.addDisposableTo(disposeBag)
    }

    /**
     登录到校园网

     - parameter username:   username
     - parameter password:   password
     - parameter captcha:    captcha
     - parameter completion: completion
     */
    public func login(username: String, password: String, captcha: String,completion: (HDAPIError?) -> Void) {
        provider
            .request(HDSchoolAPIService.Login(username: username, password: password, captcha: captcha))
            .subscribe { (event) in
                switch event {
                case .Next(let response):
                    guard let resStr = NSString(data: response.data, encoding: NSUTF8StringEncoding) else { completion(.NoData); return }
                    /**
                     * if login successed, server will return a script like this:
                     <script type="text/javascript">(opener || parent).handleLoginSuccessed();</script>
                     if it containts "sucess", we suppose user has successful logged in
                     */
                    guard let _ = resStr.lowercaseString.rangeOfString("success") else { completion(.NoData); return }
                    debugPrint("-- Thanks god, login setp 1 successed -V-")
                    /// cache username & password
                    let user = HDUser()
                    user.username = username
                    user.password = password
                    HDCache.sharedInstance.saveUser(user)
                    // parse Cookie
                    guard let res = response.response else { completion(.NoData); return }
                    let resHTTP = res as! NSHTTPURLResponse
                    guard let cookies = resHTTP.allHeaderFields["Set-Cookie"] else { completion(.NoData); return }
                    let cookieD = cookies as! String
                    HDCookieManager.sharedInstance.saveCookie(cookieD, type: .D)
                    debugPrint("-- Save CookieD: " + cookieD + " --")
                    
                    // request CookieE
                    HDAPIClient().secureCheck({ (response, error) in
                        guard let res = response else { completion(.NoData); return }
                        guard let cookie = res.allHeaderFields["Set-Cookie"] else { completion(.NoData); return }
                        HDCookieManager.sharedInstance.saveCookie(cookie as! String, type: .E)
                        debugPrint("-- Save CookieE: " + (cookie as! String) + " --");
                        // cache account info
                        let user = HDUser()
                        user.username = username
                        user.password = password
                        HDCache.sharedInstance.saveUser(user)
                        completion(nil); return
                    })
                case .Error(let error):
                    debugPrint(error)
                    completion(.Error)
                    break;
                case .Completed:
                    break;
                }
            }.addDisposableTo(disposeBag)
    }

    /**
     获取用户的真实姓名

     - parameter completion:
     */
    public func getRealName(completion: (realName: String?, error: HDAPIError?) -> Void) {
        self.provider
            .request(.GetRealName)
            .subscribe { (event) in
                switch event {
                case .Next(let response):
                    let data = response.data
                    guard let str = NSString(data: data, encoding: NSUTF8StringEncoding) else { completion(realName: nil, error: .NoData); return }
                    guard let realname = HDHTMLParser.getRealName(str as String) else { completion(realName: nil, error: .NoData); return }
                    HDCache.sharedInstance.saveRealName(realname)
                    completion(realName: realname, error: nil)
                    break
                case .Completed:
                    break
                case .Error(_):
                    completion(realName: nil, error: .NoData)
                    break
                }
            }.addDisposableTo(disposeBag)
    }

    /**
     获取培养方案信息

     - parameter completion:
     */
    public func getPYFA(completion: (pyfa: HDPYFA?, error: HDAPIError?) -> Void) {
        HDAPIClient().getPYFAID { [unowned self] (error) in
            if let _ = error { completion(pyfa: nil, error: .NoData); return }
            self.ssfwProvider
                .request(.GetPYFA)
                .subscribe({ (event) in
                    switch event {
                    case .Next(let respones):
                        do {
                            guard let json = try NSJSONSerialization.JSONObjectWithData(respones.data, options: []) as? [String:AnyObject] else {
                                completion(pyfa: nil, error: HDAPIError.ParseError); return
                            }
                            // json is ready
                            let pyfa = HDPYFA(dict: json);
                            // cache pyfa
                            HDCache.sharedInstance.savePYFA(pyfa)
                            completion(pyfa: pyfa, error: nil)
                        } catch let error as NSError {
                            print(error)
                        }
                        break;
                    case .Completed:
                        break;
                    case .Error(_):
                        completion(pyfa: nil, error: .NoData)
                        break
                    }
                }).addDisposableTo(self.disposeBag)
        }
    }

    /**
     获取成绩的 array

     - parameter completion:
     */
    public func getScores(completion: (scores: [HDScore]?, error: HDAPIError?) -> Void) {
        self.getScoreHTML { (HTMLStr, error) in
            if let html = HTMLStr {
                HDHTMLParser.getScores(html as String, completion: { (score, error) in
                    if let scr = score {
                        completion(scores: scr, error: nil)
                    } else {
                        completion(scores: nil, error: .ParseError)
                    }
                })
            }
        }
    }

    /**
     获取当前用户的成绩 HTML

     - parameter completion:
     */
    private func getScoreHTML(completion: (HTMLStr: NSString?, error: HDAPIError?) -> Void) {
        self.ssfwProvider
            .request(.GetScore)
            .subscribe{ (event) in
                switch event {
                case .Next(let response):
                    let data = response.data
                    guard let str = NSString(data: data, encoding: NSUTF8StringEncoding) else { completion(HTMLStr: nil, error: .NoData); return }
                    if str.lowercaseString.rangeOfString("500") != nil { completion(HTMLStr: str, error: nil); return }
                case .Completed:
                    break
                case .Error(_):
                    completion(HTMLStr: nil, error: .NoData); return
                }
            }.addDisposableTo(disposeBag)
    }
}




/**
 *  为了不 follow 302 的 requests，只能这么做 =,=
 *  
 *  你不需要请求这个类中的 API
 */
private class HDAPIClient: NSObject {
    typealias HDAPICompletionBlock = (data: NSData, response: NSHTTPURLResponse) -> Void
    typealias HDAPIErrorBlock = (errorMessage: String) -> Void

    override init() { }

    var sessions = Array<NSURLSession>()

    private struct ErrorMessages {
        static let ParseError = "解析失败"
        static let FormError = "用户名密码或验证码错误"
        static let ResourcesGetError = "获取失败"
    }

    // 302 就 302， 不要 follow redirect
    func secureCheck(completion: (response: NSHTTPURLResponse?, error: HDAPIError?) -> Void) {
        if let cookie = HDCookieManager.sharedInstance.getCookie(.D) {
            self.hd_schoolAPIWith(HDHLJUSecurityCheckURL, param: nil, cookie: cookie, completionBlock: { (data, response) in
                // 这里的返回没有内容
                completion(response: response, error: nil);
                }, errorBlock: { (errorMessage) in
                    completion(response: nil, error: .NoData)
            })
        } else {
            completion(response: nil, error: .NoData)
        }
    }

    // 302 就 302， 不要 follow redirect
    func getPYFAID(completion: (error: HDAPIError?) -> Void) {
        guard let cookie = HDCookieManager.sharedInstance.getCookie(.E) else { completion(error: .NoData); return }
        self.hd_schoolAPIWith(HDHLJUSSFWPYFAIDURL, param: nil, cookie: cookie, completionBlock: { (data, response) in
            if response.statusCode == 302 {
                guard let location = response.allHeaderFields["Location"] else { completion(error: .NoData); return }
                let splitArray = (location as! String).characters.split{$0 == "="}.map(String.init)
                if splitArray.count == 2 {
                    let pyfaID = splitArray[1]
                    // cache the ID
                    HDCache.sharedInstance.savePYFAID(pyfaID)
                    debugPrint("-- Save PYFAID: " + pyfaID + " --")
                    completion(error: nil); return
                }
            }
            completion(error: .NoData); return
            }, errorBlock: { (errorMessage) in
                completion(error: .NoData)
        })
    }
    
    /**
     basic request method for crawl,
     Thanks to Apple's NSURLSession, it saves the cookie,
     but we don't need it =,=
     
     - parameter URL:             the URL to request
     - parameter method:          "GET" is default
     - parameter param:
     - parameter cookie:          "asdjk:asdjkqwkjk"
     - parameter completionBlock: main queue block
     - parameter errorBlock:      main queue block
     */
    private func hd_schoolAPIWith(URL: NSURL,
                                  method: String = "GET",
                                  param: String?,
                                  cookie: String?,
                                  completionBlock: HDAPICompletionBlock,
                                  errorBlock: HDAPIErrorBlock)  {
        self.deleteAllCookies()
        self.cancelAllOperation()
        
        let request = NSMutableURLRequest(URL: URL)
        
        // set HTTPMethod
        request.HTTPMethod = method
        
        // set param
        if let para = param {
            request.HTTPBody = para.dataUsingEncoding(NSUTF8StringEncoding)
        }
        // set cookie
        if let cook = cookie {
            request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
            request.setValue(cook, forHTTPHeaderField: "Cookie")
        }

        debugPrint("-- Request ")
        debugPrint("URL: " + URL.absoluteString)
        debugPrint("Method: " + method)
        if let par = param {
            debugPrint("Param: " + par)
        }
        debugPrint("-- End --")

        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        config.timeoutIntervalForRequest = HDHLJUAPIRequestTimeoutTime
        config.timeoutIntervalForResource = HDHLJUAPIRequestTimeoutTime 
        config.HTTPCookieAcceptPolicy = .Never
        let session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)
        self.sessions.append(session)
        let task = session.dataTaskWithRequest(request) { [unowned self] (dataRet, responseRet, errorRet) in
            var resHTTPOK: NSHTTPURLResponse!
            var dataOK: NSData!
            
            if let _ = errorRet {
                self.cancelAllOperation() 
                errorBlock(errorMessage: ErrorMessages.ParseError); return;
            }
            
            // parse Response
            if let responseOK = responseRet {
                if responseOK is NSHTTPURLResponse {
                    resHTTPOK = responseOK as! NSHTTPURLResponse
                } else {
                    self.cancelAllOperation()
                    errorBlock(errorMessage: ErrorMessages.ParseError); return
                }
            } else {
                self.cancelAllOperation()
                errorBlock(errorMessage: ErrorMessages.ParseError); return
            }
            
            // parse data
            guard let dt = dataRet else {
                self.cancelAllOperation()
                errorBlock(errorMessage: ErrorMessages.ParseError); return;
            }
            
            dataOK = dt
            
            self.cancelAllOperation()
            completionBlock(data: dataOK, response: resHTTPOK); return
        }
        
        task.resume()
    }
    
    /**
     delete all cookies saved by NSURLSession
     */
    private func deleteAllCookies() {
        let cookieJar = NSHTTPCookieStorage.sharedHTTPCookieStorage()
        for cook in cookieJar.cookies! {
            cookieJar.deleteCookie(cook)
        }
    }
    
    private func cancelAllOperation() {
        for session in sessions {
            session.invalidateAndCancel()
        }
        
        sessions.removeAll()
    }
    
}

extension HDAPIClient: NSURLSessionTaskDelegate {
    @objc func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: (NSURLRequest?) -> Void) {
        // prevent redirect
        completionHandler(nil)
    }
}